#include "EPollPoller.h"
#include "Logger.h"
#include "Channel.h"

#include <errno.h>
#include <string.h>
#include <unistd.h>

// channel还没有添加到poller里，此时channel的成员index_也是-1
const int kNew = -1;
// channel已经添加到poller里了
const int kAdded = 1;  
// channel从poller中删除
const int kDeleted = 2;  


EPollPoller::EPollPoller(EventLoop* loop)
    : Poller(loop)
    , epollfd_(::epoll_create1(EPOLL_CLOEXEC))
    , events_(kInitEventListSize)  // vector<epoll_events>，epoll_wait会把发生事件的fd的event放到这个vector
{
    if(epollfd_ < 0)  // epoll创建失败
    {
        LOG_FATAL("epoll_create error: %d\n", errno);
    }
}

EPollPoller::~EPollPoller()
{
    ::close(epollfd_);
}

Timestamp EPollPoller::poll(int timeoutMs, ChannelList *activeChannels)
{
    // 通过epoll_wait监听哪些fd（channel）发生了事件，把发生事件的fd填到activeChannels告知EventLoop
    // 实际上用LOG_DEBUG更合理，因为实际情况下pool调用很频繁，INFO是必定输出的，影响效率
    LOG_INFO("func=%s → fd total count: %d\n", __FUNCTION__, static_cast<int>(channels_.size()));

    int numEvents = ::epoll_wait(epollfd_, &*events_.begin(), static_cast<int>(events_.size()), timeoutMs);

    int saveErrno = errno;  // 记录全局的errno，errno是个全局变量，多个反应堆的poll可能都会去写这个全局变量，因此先存起来、
    Timestamp now(Timestamp::now());  // 获取当前时间

    if(numEvents > 0)
    {
        LOG_INFO("%d events happened. \n", numEvents);  // LOG_DEBUG更合理
        fillActiveChannels(numEvents, activeChannels);
        if(numEvents == static_cast<int>(events_.size()))
        {
            // 准备接收就绪事件的数组满了 可能实际发生的事件还要多（数组满了放不下） → 扩容
            // 没处理的下次还会上报的，muduo库用的是LT水平模式
            events_.resize(events_.size() * 2);
        }

    }
    else if(numEvents == 0)
    {
        // 这一轮监听直到超时，都没有事件发生
        LOG_DEBUG("func=%s timeout!\n", __FUNCTION__);
    }
    else  // <0
    {
        if(saveErrno != EINTR)  // 不等于外部中断，有其他错误
        {
            errno = saveErrno;  // 主要是为了适配源码日志系统，LOG_SYSERR会访问errno，但此间errno可能会被其它线程改掉，因此改回这个函数最开始的错误值
            LOG_ERROR("EPollPoller::poll() err!\n");
        }
    }

    return now;
}

/**
 *                          EventLoop => poller.poll
 *  ChannelList(注册没注册的)             Poller
 *                          ChannelMap <fd, channel*>（注册的）
 * ChannelList里的channel数 ≥ ChannelMap里的channel数
*/

// channel update → EventLoop updateChannel → Poller updateChannel → EPollPoller updateChannel
void EPollPoller::updateChannel(Channel *channel)
{
    const int index = channel->index();  // channel->index_ 表示channel的状态（未添加、已添加、删除）
    LOG_INFO("func=%s → fd=%d events=%d index=%d\n", __FUNCTION__, channel->fd(), channel->events(), channel->index());

    if(index == kNew || index == kDeleted)
    {
        if(index == kNew)  // 当前的channel从来没被添加到poller中过
        {
            // 把channel添加到ChannelMap中
            int fd = channel->fd();
            channels_[fd] = channel;
        }
        else  // index == kDeleted
        {
            // assert(channels_.find(fd) != channels_.end());
            // assert(channels_.find(fd) == channel);
        }

        channel->setIndex(kAdded);
        update(EPOLL_CTL_ADD, channel);  // EPOLL_CTL_ADD
    }
    else  // index == kAdded，channel已经在poller上注册过了
    {
        int fd = channel->fd();
        if(channel->isNoneEvent())
        {
            // channel已经对任何事件都不感兴趣了，删掉
            update(EPOLL_CTL_DEL, channel);  // EPOLL_CTL_DEL
            channel->setIndex(kDeleted);
        }
        else
        {
            update(EPOLL_CTL_MOD, channel);  // EPOLL_CTL_MOD
        }
    }
}

// channel remove → EventLoop removeChannel → Poller removeChannel
// 从poller中删除channel
void EPollPoller::removeChannel(Channel *channel)
{
    // 从poller中remove就是从ChannelMap中remove
    // 如果channel对应的状态是added，已经向epoll注册了，还得从epoll里删除
    
    int fd = channel->fd();
    channels_.erase(fd);

    LOG_INFO("func=%s → fd=%d event=%d \n", __FUNCTION__, fd, channel->events());

    int index = channel->index();  // channel在poller中的状态
    if(index == kAdded)  // 已经注册过了
    {
        update(EPOLL_CTL_DEL, channel);
    }
    channel->setIndex(kNew);

}

// 填写活跃的channel
void EPollPoller::fillActiveChannels(int numEvents, ChannelList *activeChannels) const
{
    // update(int operation, Channel *channel)中设置的时候，设置了fd，同时channel作为附带信息设置到ptr上了
    // 所以这里，能直接拿到发生fd对应的Channel
    for(int i=0; i<numEvents; i++)  // numEvents是epoll_wati的返回值，发生事件的fd的数量
    {
        Channel* channel = static_cast<Channel*> (events_[i].data.ptr);  // events_[i].data.ptr是void*
        channel->set_revents(events_[i].events);  // 设置发生的事件
        activeChannels->push_back(channel);
    }  // EventLoop就拿到了它的poller给它返回的所有发生事件的Channel列表
}

// 更新channel通道 epoll_ctl add/mod/del
void EPollPoller::update(int operation, Channel *channel)
{
    // 重新定义了一个epoll_event用于更新
    epoll_event event;
    // memset(&event, 0, sizeof(event));  // 也可以
    bzero(&event, sizeof event);

    int fd = channel->fd();
    event.events = channel->events();  // 感兴趣的事件
    event.data.fd = fd;
    event.data.ptr = channel;

    LOG_INFO("epoll_ctl op=%d fd=%d event=%d\n", operation, channel->fd(), channel->events());

    if(::epoll_ctl(epollfd_, operation, fd, &event) < 0)
    {
        // < 0，出错了
        if(operation == EPOLL_CTL_DEL)
        {
            LOG_ERROR("epoll_ctl delete error: %d\n", errno);
        }
        else  // add mod出错了
        {
            LOG_FATAL("epoll_ctl add/mod:%d error: %d\n", operation, errno);
        }
    }
}